/*!
 * jQuery Plugin to track YouTube video events from YouTube iFrame embeds to 
 * Google Analytics or other analytics tool. This also can create YouTube iFrame 
 * embeds.
 *
 * Copyright 2012 Ronn Abueg / Work at Play
 * 
 * Licensed under the MIT license:
 *   http://www.opensource.org/licenses/mit-license.php
 */
jQuery.ytiframetracker = function(_settings) {
  // Defaults
  var settings = jQuery.extend({
    selector: 'iframe[src*="youtube.com/embed/"]', // iframe embed selector
    createSelector: '.ytvid', // elements with this selector will become embeds
    createVidSource: function (e) { // where in the element to find the vid
      return $(e).html();
    },
    idPrefix: 'ytvid-', // prefix for dynamically created embed id
    loadApi: '//www.youtube.com/player_api', // url or false to not load api
    eventCategory: 'Youtube-video', // event category 
    eventLabel: 'video_id', // or title
    width: 640, // default dimension
    height: 390, 
    track: function (action, label, value) { // track callback
      track(action, label, value);
    },
    log: false // for debugging
  }, _settings);
  var players = {}; // player objects
  var videoData = {}; 
  var playerCounter = 0;
  var watching = {};
  
  // initialization: build iframes and/or create object for existing iframes
  var init = function (context) {
    if (context) {
      context = document;
    }
    jQuery(settings.selector+','+settings.createSelector, context).each(function () {
      initPlayer(this);
    });
  };
  
  // init/create player; call optional func
  var initPlayer = function (e, func) {
    if (e.length) { // jquery object
      e = e[0];
    }
    if (!e.id) {
      e.id = settings.idPrefix + playerCounter++;
    }
    var id = e.id;
    
    $(e).attr('data-yid', id);
    if (!players[id]) {
      var playerSettings = {
        events: {
          onReady: function (event) {
            if (func) {
              func(event.target);
            } else { // shouldn't do both
              $(e).trigger('youTubePlayerReady', [event.target]);
            }
          },
          onStateChange: function (event) {
            stateChange(event);
          }
        }
      };
      if (e.tagName != 'IFRAME') { // need to create iframe
        var vid = settings.createVidSource(e);
        if (vid) {
          playerSettings.videoId = vid;
          playerSettings.width = $(e).attr('width') // reuse width
            ? $(e).attr('width') : settings.width;
          playerSettings.height = $(e).attr('height') // reuse height
            ? $(e).attr('height') : settings.height;
        }
      }
      if (window.YT && window.YT.Player) {
        players[id] = new YT.Player(id, playerSettings);      
      }
    }
    if (players[id]) {
      debug(id, players[id]);      
      return players[id];
    }
  }

  // state change callback
  var stateChange = function (event) {
    var action = false;
       
    switch (event.data) {
      case YT.PlayerState.CUED:
        action = 'cued';
        break;
      case YT.PlayerState.PLAYING:
        action = 'played';
        break;
      case YT.PlayerState.PAUSED:
        action = 'paused';
        break;
      case YT.PlayerState.ENDED:
        action = 'ended';
        break;
    }
    
    if (action && event.target && event.target.getVideoUrl) {
      var _track = function (action, id, value) {
        var __track = function (id, action, value) {
          var label = videoData[id][settings.eventLabel] 
            ? videoData[id][settings.eventLabel] : id;
          settings.track(action, label, value);
        }
          
        if (!videoData[id]) {
          $.getJSON('http://gdata.youtube.com/feeds/api/videos/'+id+'?v=2&alt=jsonc', function(data) {
            if (data.data) {
              videoData[id] = data.data;
              __track(id, action, value);
            }
          });          
        } else {
          __track(id, action, value);
        }
      }
      
      var id = event.target.getVideoUrl();
      id = id.substring(id.indexOf('v=')+2);
      if (id.indexOf('&')>-1) {
        id = id.substring(0, id.indexOf('&'));
      }
      var label = id;
      var ytime = event.target.getCurrentTime 
        ? event.target.getCurrentTime() : 0;
          
      if (event.target.a.id) {
        if (!players[event.target.a.id]) { // shouldn't happen but just in case
          players[event.target.a.id] = event.target; // add it
        }
        
        // make id more unique in case we have multiple players of the same 
        // video and/or a player that plays the next video
        id += event.target.a.id; 
      } 
      
      if (!watching[id]) {
        watching[id] = {};
      } 
      
      // track typical and non-repeating action/event
      if (!watching[id].prev 
        || watching[id].prev.action != action) {
        _track(action, label, Math.round(ytime));
      }
      
      // track advanced action/event
      if (watching[id].prev) {
        if (watching[id].prev.action == action) {
          if (typeof watching[id].repeat == 'undefined') {
            watching[id].repeat = watching[id].prev.cs;
          }
        }
        var diff = Math.round(Math.abs(ytime - watching[id].prev.cs));
        // track duration watched
        if (watching[id].prev.action == 'played' 
            && (action == 'paused' || action == 'ended')
            && diff > 0) {
          _track('watched', label, diff);
        } 
        // track rewind/fastforward
        if (action == 'played') {
          var last = watching[id].repeat;
          diff = Math.round(Math.abs(last - ytime));
          if (diff > 0) {
            _track(last < ytime 
              ? 'fast-forward' : 'rewind', label, diff);
          }
          delete watching[id].repeat;
        }
      }
      
      watching[id].prev = {
        label: label,
        action: action,
        cs: ytime
      };
    }
  }

  // track some action; can be overridden
  var track = function (action, label, value) {
    if (window._gaq) {
      _gaq.push(['_trackEvent', settings.eventCategory, action, label, value]);
    }
    debug('_trackEvent', settings.eventCategory, action, label, value);
  }
  
  // debugging
  var debug = function () {
    if (settings.log && window.console && window.console.log) {
      console.log('ytiframetracker', arguments);
      return true;
    }
    return false;
  }

  // load api if needed
  if (settings.loadApi 
    && jQuery('script[src*="'+settings.loadApi+'"]').length == 0) {
    var tag = jQuery('<script>').attr('src', settings.loadApi);
    jQuery('script:first').before(tag);
    settings.loadApi = false;

    debug('added player api ' + settings.loadApi);
  }
  
  // implement onYouTubePlayerAPIReady if possible
  if (!window.onYouTubeIframeAPIReady) {
    window.onYouTubeIframeAPIReady = function () {
      $(document).trigger('youTubePlayerAPIReady', []); 
      // bind on this if you have code that relies on the api
    }
  } // else, you need to call .init(document);
  
  // api ready, init iframe embeds
  $(document).bind('youTubePlayerAPIReady', function () {
    debug('youTubePlayerAPIReady');
    init(document);
    // for iframes created after, call .initPlayer(element)
  }); 
  
  // leaving page, stop player(s)
  if (!window.onbeforeunload) {
    window.onbeforeunload = function() {
      // $(window).bind('beforeunload') not ideal
      for (var i in players) {
        var player = players[i];
        if (player.pauseVideo) { // stop all players
          player.pauseVideo();
        }
      }   
      if (debug('leaving')) {
        alert('wait');
      }
    }
  }
  
  return { // usefule functions
    init: init, // initialize players, pass it document or some element
    initPlayer: initPlayer, // initialize/create a player
    stateChange: stateChange, // callback for state change
    players: players // youtube iframe players
  };    
};